////////////////////////////////////////////////////////////////
//
//       Filename:  FrameReportView.go
//
//        Version:  1.0
//        Created:  2022年11月16日 23时06分48秒
//       Revision:  none
//       Compiler:  go
//
//         Author:  alpha
//   Organization:  alpha
//       Contacts:  chenxinquan@kylinos.com
//
////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
// Description:
////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
// Log:
////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
// Todo:
//
////////////////////////////////////////////////////////////////

package gconsole

import (
    "bytes"
    "fmt"
    //"strings"
    "errors"
    //"reflect"
    "text/template"
    "github.com/jesseduffield/gocui"
)

const (
    FRAMEREPORTVIEW_VIEWNAME_TARGET         string = "target"
    FRAMEREPORTVIEW_VIEWNAME_VULN           string = "vuln"
    FRAMEREPORTVIEW_VIEWNAME_VULNINFO       string = "vulninfo"
    FRAMEREPORTVIEW_VIEWNAME_REPORT         string = "report"
)

type
ViewRact struct {
    Left            int
    Top             int
    Right           int
    Bottom          int
}

///////////////////////////////
// layout ract
func
VIEWRACT_TARGET(maxx int, maxy int) ViewRact {
    return ViewRact{(-1),
                    (-1),
                    32,
                    maxy    }
}
func
VIEWRACT_VULN(maxx int, maxy int) ViewRact {
    return ViewRact{32,
                    -1,
                    maxx,
                    int(float64(maxy)*0.3)  }
}
func
VIEWRACT_VULNINFO(maxx int, maxy int) ViewRact {
    return ViewRact{32 + (int(float64(maxx - 32)*0.5)),
                    (int(float64(maxy)*0.3)),
                    maxx,
                    maxy                            }
}
func
VIEWRACT_REPORT(maxx int, maxy int) ViewRact {
    return ViewRact{32,
                    (int(float64(maxy)*0.3)),
                    32 + (int(float64(maxx - 32)*0.5)),
                    maxy                            }
}
// layout ract
///////////////////////////////

func
(fr *FrameReport)SetTargetLayout(g *gocui.Gui) error {
    viewract := VIEWRACT_TARGET(g.Size())

    view, rc := g.SetView(FRAMEREPORTVIEW_VIEWNAME_TARGET,
                          viewract.Left,   viewract.Top,
                          viewract.Right, viewract.Bottom);
    /* */
    if (nil != rc) {
        if (gocui.ErrUnknownView != rc) {
            return rc
        }

        view.Highlight = true
        view.Editable = false
        view.Wrap = false

        for _, rpt := range fr.reports {
            // TODO:
            fmt.Fprintln(view,
                         rpt.GetReportCommon().RCServerName +
                         ":" +
                         rpt.GetReportCommon().RCExploredIPv6Addrs[0] +
                         ":" +
                         rpt.GetReportCommon().RCExploredIPv4Addrs[0] +
                         ":" +
                         rpt.GetReportCommon().RCExploredType           )
        } // for _, rpt ...

        if ( 0 == len(fr.reports) ) {
            A_DEBUG_WARNING("No report!")
            /* */
            // TODO
            return errors.New("No report!")
        }
        fr.curReport = fr.reports[0]


        rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_TARGET)
        /* */
        if (nil != rc) {
            return rc
        } // if (nil ...
    } // if (nil != rc ...

    return nil
}

func
(fr *FrameReport)SetVulnLayout(g *gocui.Gui) error {
    viewract := VIEWRACT_VULN(g.Size())

    view, rc := g.SetView(FRAMEREPORTVIEW_VIEWNAME_VULN,
                          viewract.Left,  viewract.Top,
                          viewract.Right, viewract.Bottom);
    /* */
    if (nil != rc) {
        if (gocui.ErrUnknownView != rc) {
            return rc
        }

        view.Highlight = true
        view.Editable = false
        view.Wrap = false

        // TODO:漏洞能根据分数高低排一下序就更好了
        // TODO:过滤一下值中的换行符
        ///////////////
        //
        vuls := fr.curReport.GetReportCommon().RCExploredVulns

        indexFormat := ""
        /* */
        if len(vuls) < 10 {
            indexFormat = "[%1d]"
        } else if len(vuls) < 100 {
            indexFormat = "[%2d]"
        } else {
            indexFormat = "[%3d]"
        } // if len ...

        // TODO: the initial number:0, not that precise
        for i, vinfo := range vuls {
            line :=
                fmt.Sprintf(indexFormat, i+1) +
                fmt.Sprintf("%-20s", vinfo.VICId) +
                vinfo.VICPocHazardLevel + " | " +
                fmt.Sprintf("%-6s | ", vinfo.VICBelong) +
                fmt.Sprintf("%10s | ", vinfo.VICSiteInfo.Name) +
                fmt.Sprintf("%4s | ",  vinfo.VICSiteInfo.Severity) +
                vinfo.VICSiteInfo.Description                       ;

            fmt.Fprintln(view, line)
        } // for _, vinfo ...
    } // if (nil ...

    return nil
}

func
(fr *FrameReport)SetVulnInfoLayout(g *gocui.Gui) error {
    viewract := VIEWRACT_VULNINFO(g.Size())

    view, rc := g.SetView(FRAMEREPORTVIEW_VIEWNAME_VULNINFO,
                          viewract.Left,  viewract.Top,
                          viewract.Right, viewract.Bottom);
    /* */
    if (nil != rc) {
        if (gocui.ErrUnknownView != rc) {
            return rc
        }

        expvul :=  fr.curReport.GetReportCommon().RCExploredVulns
        /* */
        if ( 0 != len(expvul) ) {
            // TODO: the initial number:0, not that precise
            tmpl, rc_ := template.New("VulnInfo").Parse(mdVulnInfoTmpl)
            rc = rc_
            /* */
            if nil != rc {
                return rc
            } // if nil ...

            buf := bytes.NewBuffer(nil)
            rc = tmpl.Execute(buf, expvul[fr.curVuln])
            /* */
            if nil != rc {
                return rc
            } // if nil ...

            fmt.Fprintln(view, buf)

        } // if (0 != ...

        view.Editable = false
        view.Wrap = false
    }

    return nil
}

func
(fr *FrameReport)SetReportLayout(g *gocui.Gui) error {
    viewract := VIEWRACT_REPORT(g.Size())

    view, rc := g.SetView(FRAMEREPORTVIEW_VIEWNAME_REPORT,
                          viewract.Left,  viewract.Top,
                          viewract.Right, viewract.Bottom);

    /* */
    if (nil != rc) {
        if (gocui.ErrUnknownView != rc) {
            return rc
        }

        tmpl, rc_ := template.New("Report").Parse(mdReportTmpl)
        rc = rc_
        /* */
        if nil != rc {
            return rc
        } // if nil ...

        expvul :=  fr.curReport.GetReportCommon().RCExploredVulns
        /* */
        if ( 0 != len(expvul) ) {
            // TODO: the initial number:0, not that precise

        } // if (0 != ...

        buf := bytes.NewBuffer(nil)
        rc = tmpl.Execute(buf, fr.curReport.GetReportCommon())
        /* */
        if nil != rc {
            return rc
        } // if nil ...

        fmt.Fprintln(view, buf)

        view.Editable = false
        view.Wrap = false
    }

    return nil
};

///////////////////////////////
// navigation
func
(fr *FrameReport)NextView(g *gocui.Gui, v *gocui.View) error {
    var rc error

    if (nil == v) {
        return g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_TARGET)
    } // if (nil == ...

    switch ( v.Name() ) {
        case FRAMEREPORTVIEW_VIEWNAME_TARGET:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_VULN)
        case FRAMEREPORTVIEW_VIEWNAME_VULN:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_VULNINFO)
        case FRAMEREPORTVIEW_VIEWNAME_VULNINFO:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_REPORT)
        case FRAMEREPORTVIEW_VIEWNAME_REPORT:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_TARGET)
        default:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_VULN)
    } // switch ( v ...

    return rc
}
func
(fr *FrameReport)PreView(g *gocui.Gui, v *gocui.View) error {
    var rc error

    if (nil == v) {
        return g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_TARGET)
    } // if (nil == ...

    switch ( v.Name() ) {
        case FRAMEREPORTVIEW_VIEWNAME_TARGET:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_REPORT)
        case FRAMEREPORTVIEW_VIEWNAME_REPORT:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_VULNINFO)
        case FRAMEREPORTVIEW_VIEWNAME_VULNINFO:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_VULN)
        case FRAMEREPORTVIEW_VIEWNAME_VULN:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_TARGET)
        default:
            rc = g.SetCurrentView(FRAMEREPORTVIEW_VIEWNAME_TARGET)
    } // switch ( v ...

    return rc
}
/* */
func
(fr *FrameReport)NextVulnItem(g *gocui.Gui, v *gocui.View) error {
    return nil
}
func
(fr *FrameReport)PreVulnItem(g *gocui.Gui, v *gocui.View) error {
    return nil
}
/* */
func
(fr *FrameReport)CursorDown(g *gocui.Gui, v *gocui.View) error {
    if (nil != v) {
        cx, cy := v.Cursor()
        ox, oy := v.Origin()
        ok, _  := fr.movable(v, oy+cy+1)

        if ( ! ok) {
            return nil
        } // if ( ! ok ..

        rc := v.SetCursor(cx, (cy + 1) )
        /* */
        if (nil != rc) {
            rc = v.SetOrigin(ox, (oy + 1) )
            /* */
            if (nil != rc) {
                return rc
            } // if (nil != ...
        } // if (nil != ...

        rc = fr.onMovingCursorRedrawView(g, v)
        /* */
        if (nil != rc) {
            return rc
        } // if (nil != ...
    } // if (nil != ...

    //cx, cy := v.Cursor()
    //ox, oy := v.Origin()

    // TODO:debug
    // A_DE

    return nil
}
func
(fr *FrameReport)CursorUp(g *gocui.Gui, v *gocui.View) error {
    if (nil != v) {
        ox, oy := v.Origin()
        cx, cy := v.Cursor()

        rc := v.SetCursor(cx, cy-1)
        if ( (nil != rc) &&
             (0 < oy)       ) {
            rc = v.SetOrigin(ox, oy-1)
            if (nil != rc) {
                return rc
            } // if (nil != ...
        } // if ( (nil != rc ..
    } // if (nil != ...

    _ = fr.onMovingCursorRedrawView(g, v)

    /* */
    return nil
}
func
(fr *FrameReport)CursorPageDown(g *gocui.Gui, v *gocui.View) error {
    jump := fr.pageUpDownJumpCount(v)

    if v == nil {
        return nil
    }

    cx, cy  := v.Cursor()
    ox, oy  := v.Origin()
    ok, yLimit := fr.movable(v, oy + cy + jump)
    _, maxY := v.Size()

    if !ok {
        if yLimit < maxY {
            _ = v.SetCursor(cx, yLimit)
        } else {
            _ = v.SetCursor(cx, maxY - 1)
            _ = v.SetOrigin(ox, yLimit - maxY - 1)
        } // if yLimit ... else ...
    } else if yLimit < oy + jump + maxY { // if !ok
        if yLimit < maxY {
            _ = v.SetCursor(cx, yLimit)
        } else {
            _ = v.SetOrigin(ox, yLimit - maxY + 1)
            _ = v.SetCursor(cx, maxY - 1)
        } // if yLimit ... else ...
    } else { // else if yLimit ...
        _ = v.SetCursor(cx, cy)
        if err := v.SetOrigin(ox, oy + jump); err != nil {
            return err
        }
    } // if !ok ... else ... else ...

    _ = fr.onMovingCursorRedrawView(g, v)

    return nil
}
func
(fr *FrameReport)CursorPageUp(g *gocui.Gui, v *gocui.View) error {
    jump := fr.pageUpDownJumpCount(v)

    if v == nil {
        return nil
    }

    cx, _  := v.Cursor()
    ox, oy := v.Origin()
    /* */
    if err := v.SetOrigin(ox, oy - jump); err != nil {
        if err := v.SetOrigin(ox, 0); err != nil {
            return err
        } // if err ...

        _ = v.SetCursor(cx, 0)
    } // if err ...

    _ = fr.onMovingCursorRedrawView(g, v)

    return nil
}


///////////////////////////////
// Utils
func
(fr *FrameReport)movable(v *gocui.View, nextY int) (ok bool, yLimit int) {
    switch ( v.Name() ) {
        case FRAMEREPORTVIEW_VIEWNAME_TARGET:
            yLimit = len(fr.reports) - 1
            /* */
            if (yLimit < nextY) {
                return false, yLimit
            } // if (yLimit ...
            /* */
            return true, yLimit
        case FRAMEREPORTVIEW_VIEWNAME_VULN:
            yLimit = len(fr.curReport.GetReportCommon().RCExploredVulns) - 1
            if (yLimit < nextY) {
                return false, yLimit
            } // if (yLimit ...
            /* */
            return true, yLimit
        case FRAMEREPORTVIEW_VIEWNAME_VULNINFO:
            // TODO:
            // return true, fr.curVulnViewLimitY
            return true, 2000
        case FRAMEREPORTVIEW_VIEWNAME_REPORT:
            // TODO:
            // return true, fr.curReportViewLimitY
            return true, 2000
        default:
            return true, 0
    } // switch ( ...
}

// redraw views
func
(fr *FrameReport)onMovingCursorRedrawView(g *gocui.Gui, v *gocui.View) error {
    switch ( v.Name() ) {
        case FRAMEREPORTVIEW_VIEWNAME_TARGET:
            if rc := fr.redrawViewTarget(g, v); (nil != rc) {
                return rc
            }
        case FRAMEREPORTVIEW_VIEWNAME_VULN:
            // we don't need to redraw
            // if rc := fr.redrawViewVuln(g, v); (nil != rc) {
            //    return rc
            // } // if rc := ...
            // /* */
            // we just need to chage fr.curVuln
            _, cy := v.Cursor()
            _, rc_t := v.Line(cy)
            rc := rc_t
            if (nil != rc) {
                return rc
            } // if (nil != ...
            // TODO: 这样不够准确
            fr.curVuln = cy

            if rc := fr.redrawViewVulnInfo(g); (nil != rc) {
                return rc
            } // if rc := ...
            if rc := fr.redrawViewReport(g); (nil != rc) {
                return rc
            } // if rc := ...
    } // switch ( v.Name() ...

    return nil
}

func
(fr *FrameReport)redrawViewTarget(g *gocui.Gui, v *gocui.View) error {
    rc := g.DeleteView(FRAMEREPORTVIEW_VIEWNAME_REPORT)
    /* */
    if (nil != rc) {
        return rc
    } // if (nil ...

    rc = g.DeleteView(FRAMEREPORTVIEW_VIEWNAME_VULNINFO)
    /* */
    if (nil != rc) {
        return rc
    } // if (nil ...

    rc = g.DeleteView(FRAMEREPORTVIEW_VIEWNAME_VULN)
    /* */
    if (nil != rc) {
        return rc
    } // if (nil ...

    _, cy := v.Cursor()
    _, rc_t := v.Line(cy)
    rc = rc_t
    if (nil != rc) {
        return rc
    } // if (nil != ...

    //targetname := strings.TrimSpace(l)

    // TODO: 这样不够准确
    fr.curReport = fr.reports[cy]

    //for _, r := range fr.reports {
    //}

    rc = fr.SetVulnLayout(g);
    /* */
    if (nil != rc) {
        return rc
    }

    rc = fr.SetVulnInfoLayout(g);
    /* */
    if (nil != rc) {
        return rc
    }
    rc = fr.SetReportLayout(g);
    if (nil != rc) {
        return rc
    }

    return rc
}

func
(fr *FrameReport)redrawViewVuln(g *gocui.Gui, v *gocui.View) error {
    rc := g.DeleteView(FRAMEREPORTVIEW_VIEWNAME_VULN)
    /* */
    if (nil != rc) {
        return rc
    } // if (nil ...

    _, cy := v.Cursor()
    _, rc_t := v.Line(cy)
    rc = rc_t
    if (nil != rc) {
        return rc
    } // if (nil != ...

    //targetname := strings.TrimSpace(l)

    // TODO: 这样不够准确
    fr.curVuln = cy

    return fr.SetVulnLayout(g)
}

func
(fr *FrameReport)redrawViewVulnInfo(g *gocui.Gui) error {
    rc := g.DeleteView(FRAMEREPORTVIEW_VIEWNAME_VULNINFO)
    /* */
    if (nil != rc) {
        return rc
    } // if (nil ...

    return fr.SetVulnInfoLayout(g)
}

func
(fr *FrameReport)redrawViewReport(g *gocui.Gui) error {
    rc := g.DeleteView(FRAMEREPORTVIEW_VIEWNAME_REPORT)
    /* */
    if (nil != rc) {
        return rc
    } // if (nil ...

    return fr.SetReportLayout(g)
}

///////////////////////////////
// Auxiliray Function
func
(fr *FrameReport)pageUpDownJumpCount(v *gocui.View) int {
    var jump int

    switch v.Name() {
        case FRAMEREPORTVIEW_VIEWNAME_TARGET:
            jump = 8
        case FRAMEREPORTVIEW_VIEWNAME_VULN:
            jump = 8
        case FRAMEREPORTVIEW_VIEWNAME_VULNINFO:
            jump = 10
        case FRAMEREPORTVIEW_VIEWNAME_REPORT:
            jump = 10
        default:
            jump = 1
    } // switch v.Name ...

    return jump
}

///////////////////////////////
// 字面量

// connect to struct 'VulnInfoCommon'
const mdVulnInfoTmpl = `
===================
{{.VICId}}
===================

VICFormatVer : {{.VICFormatVer}}
VICId : {{.VICId}}
VICBelong : {{.VICBelong}}
VICPocHazardLevel : {{.VICPocHazardLevel}}
VICSource : {{.VICSource}}

    -------------------
    -- SiteInfo
    -------------------
    Name : {{.VICSiteInfo.Name}}
    Severity : {{.VICSiteInfo.Severity}}
    Description : {{.VICSiteInfo.Description}}
    ScopeOfInfluence : {{.VICSiteInfo.ScopeOfInfluence}}
    References : {{.VICSiteInfo.References}}
    SiteClassification : {{.VICSiteInfo.SiteClassification}}
    Tags : {{.VICSiteInfo.Tags}}

    -------------------
    -- SiteRequests
    -------------------
    ImArray : {{.VICSiteRequests.Implement.ImArray}}
    ExpireTime : {{.VICSiteRequests.Implement.ExpireTime}}
    Inter : {{.VICSiteRequests.Implement.Inter}}
    Condition : {{.VICSiteRequests.Implement.Condition}}
`
// connect to struct 'ReportCommon'
const mdReportTmpl = `
===================
 Report
===================

RCServerUUID : {{.RCServerUUID}}
RCServerName : {{.RCServerName}}
RCFamily : {{.RCFamily}}
RCRelease : {{.RCRelease}}
RCContainer : {{.RCContainer}}

RCExploredType : {{.RCExploredType}}
RCExploredTimeAt : {{.RCExploredTimeAt}}
RCExploredMode : {{.RCExploredMode}}
RCExploredVersion : {{.RCExploredVersion}}
RCExploredRevision : {{.RCExploredRevision}}
RCExploredBy : {{.RCExploredBy}}
RCExploredVia : {{.RCExploredVia}}
RCExploredIPv4Addrs : {{.RCExploredIPv4Addrs}}
RCExploredIPv6Addrs : {{.RCExploredIPv6Addrs}}

RCReportedAt : {{.RCReportedAt}}
RCReportedVersion : {{.RCReportedVersion}}
RCReportedBy : {{.RCReportedBy}}

RCElapsedTime : {{.RCElapsedTime}}

RCErrors : {{.RCErrors}}
RCWarnings : {{.RCWarnings}}

RCExploredVulns : {{.RCExploredVulns}}
RCReunningKernelInfo : {{.RCReunningKernelInfo}}
RCPackages : {{.RCPackages}}
RCSrcPackages : {{.RCSrcPackages}}
RCOptional : {{.RCOptional}}
`

///////////////////////////////
// z-trash
//    ///////////////
//    // lambda
//    // print indent lambda
//    pil := func(view_ *gocui.View, indent int) {
//        for i := 0; i < indent; i++ {
//            fmt.Fprint(view_, "  ")
//        } // for i_ ...
//    }
//
//    // print struct recursively lambda
//    var psrl func(view_ *gocui.View, indent int, s interface{}) error
//    psrl = func(view_ *gocui.View, indent int, s interface{}) error {
//
//        t := reflect.TypeOf(s)
//        v := reflect.ValueOf(s)
//
//        fmt.Fprintln(view_, "")
//        fmt.Fprintln(view_, "-------------------")
//        fmt.Fprintln(view_, "-- ", t.Name() )
//        fmt.Fprintln(view_, "-------------------")
//
//        for i := 0; i < t.NumField(); i++ {
//            s_ := v.Field(i).Interface()
//            t_s_ := reflect.TypeOf(s_)
//            /* */
//            switch (t_s_.Kind()) {
//                case reflect.Struct:
//                    indent++
//
//                    psrl(view_, indent, s_)
//                default:
//                    pil(view_, indent); fmt.Fprintln(view_,
//                                              t.Field(i).Name,
//                                              ":",
//                                              v.Field(i).Interface())
//            } // switch (t_s_ ...
//        } // for i := 0 ...
//
//        return nil
//    }
//    // lambda
//    ///////////////
//
//    ///////////////
//    // lambda
//    // print indent lambda
//    pil := func(view_ *gocui.View, indent int) {
//        for i := 0; i < indent; i++ {
//            fmt.Fprint(view_, "  ")
//        } // for i_ ...
//    }
//
//    // print struct lambda
//    var psl func(view_ *gocui.View, s interface{}) error
//    psl = func(view_ *gocui.View, s interface{}) error {
//        t := reflect.TypeOf(s)
//        v := reflect.ValueOf(s)
//
//        for i := 0; i < t.NumField(); i++ {
//            s_   := v.Field(i).Interface()
//            t_s_ := reflect.TypeOf(s_)
//            /* */
//            if (reflect.Struct != t_s_.Kind()) {
//                if ("RCExploredVulns" != t.Field(i).Name) {
//                    line := fmt.Sprintf(
//                                "%-20s : %-20s",
//                                t.Field(i).Name,
//                                v.Field(i).Interface() )
//                fmt.Fprintln(view_, line)
//            }
//            } // if (reflect ...
//        } // for i := ...
//
//        return nil
//    }
//
//    // print struct recursively lambda
//    var psrl func(view_ *gocui.View, indent int, s interface{}) error
//    psrl = func(view_ *gocui.View, indent int, s interface{}) error {
//
//        t := reflect.TypeOf(s)
//        v := reflect.ValueOf(s)
//
//        fmt.Fprintln(view_, "")
//        fmt.Fprintln(view_, "-------------------")
//        fmt.Fprintln(view_, "-- ", t.Name() )
//        fmt.Fprintln(view_, "-------------------")
//
//        for i := 0; i < t.NumField(); i++ {
//            s_   := v.Field(i).Interface()
//            t_s_ := reflect.TypeOf(s_)
//            /* */
//            switch (t_s_.Kind()) {
//                case reflect.Struct:
//                    indent++
//
//                    psrl(view_, indent, s_)
//                default:
//                    pil(view_, indent); fmt.Fprintln(view_,
//                                              t.Field(i).Name,
//                                              ":",
//                                              v.Field(i).Interface())
//            } // switch (t_s_ ...
//        } // for i := 0 ...
//
//        return nil
//    }
//    // lambda
//    ///////////////
